package json2xml;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeProperty;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/*
 translate from
 {
  "description" : "An imaginary server config file",
  "logs" : {"level":"verbose", "dir":"/var/log"},
  "host" : "antlr.org",
  "admin": ["parrt", "tombu"],
  "aliases": []
}

to

<description>An imaginary server config file</description>
<logs>
<level>verbose</level>
<dir>/var/log</dir>
</logs>
<host>antlr.org</host>
<admin>
<element>parrt</element>
<element>tombu</element>
</admin>
<aliases></aliases>

 */

public class JSON2XML {

    public static class XMLEmitter extends JSONBaseListener {
        // container
        ParseTreeProperty<String> xml = new ParseTreeProperty<>();
        String getXML(ParseTree parseTree) {
            return xml.get(parseTree);
        }
        void setXML(ParseTree parseTree, String s) {
            xml.put(parseTree, s);
        }

        public void exitJson(JSONParser.JsonContext context) {
            setXML(context, getXML(context.getChild(0)));
        }

        public void exitAnObject(JSONParser.AnObjectContext context) {
            StringBuilder builder = new StringBuilder();
            builder.append("\n");
            for (JSONParser.PairContext pairContext : context.pair()) {
                builder.append(getXML(pairContext));
            }
            setXML(context, builder.toString());
        }

        public void exitEmptyObject(JSONParser.EmptyObjectContext context) {
            setXML(context, "");
        }

        public void exitArrayOfValues(JSONParser.ArrayOfValuesContext context) {
            StringBuilder builder = new StringBuilder();
            builder.append("\n");
            for (JSONParser.ValueContext valueContext : context.value()) {
                builder.append("<element>");
                builder.append(getXML(valueContext));
                builder.append("</element>");
                builder.append("\n");
            }
            setXML(context, builder.toString());
        }

        public void exitEmptyArray(JSONParser.EmptyArrayContext context) {
            setXML(context, "");
        }

        public void exitPair(JSONParser.PairContext context) {
            String tag = stripQuotes(context.STRING().getText());
            JSONParser.ValueContext valueContext = context.value();
            String x = String.format("<%s>%s</%s>\n", tag, getXML(valueContext), tag);
            setXML(context, x);
        }

        public void exitObjectValue(JSONParser.ObjectValueContext context) {
            // analogous to String value() { return object() }
            setXML(context, getXML(context.object()));
        }

        public void exitArrayValue(JSONParser.ArrayValueContext context) {
            setXML(context, getXML(context.array()));
        }

        public void exitAtom(JSONParser.AtomContext context) {
            setXML(context, context.getText());
        }

        public void exitString(JSONParser.StringContext context) {
            setXML(context, stripQuotes(context.getText()));
        }

        public static String stripQuotes(String s) {
            if (s == null || s.charAt(0) != '"') {
                return s;
            }
            return s.substring(1, s.length()-1);
        }

    }

    public static void main(String[] args) throws IOException {
        String inputFile = null;
        if (args.length > 0) {
            inputFile = args[0];
        }
        InputStream is = System.in;
        if (inputFile != null) {
            is = new FileInputStream(inputFile);
        }
        CharStream input = CharStreams.fromStream(is);
        JSONLexer lexer = new JSONLexer(input);
        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
        JSONParser parser = new JSONParser(tokenStream);
        parser.setBuildParseTree(true);
        ParseTree tree = parser.json();

        ParseTreeWalker walker = new ParseTreeWalker();
        XMLEmitter converter = new XMLEmitter();
        walker.walk(converter, tree);
        System.out.println(converter.getXML(tree));
    }
}
